﻿using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace FCSChart.Axis
{
    /// <summary>
    /// 轴分割方式
    /// </summary>
    public abstract class IAxis : Control
    {
        #region property
        /// <summary>
        /// 数据集合
        /// </summary>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            protected set { SetValue(ItemsSourceProperty, value); }
        }
        public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(IAxis), new PropertyMetadata(null));

        public string AxisName { get; protected set; }
        /// <summary>
        /// 最大值
        /// </summary>
        public double Max
        {
            get { return (double)GetValue(MaxProperty); }
            internal set { SetValue(MaxProperty, value); }
        }
        public static readonly DependencyProperty MaxProperty = DependencyProperty.Register("Max", typeof(double), typeof(IAxis), new PropertyMetadata(100000d, (sender, e) => { if (sender is IAxis axis && e.NewValue is double d) { axis.MaxAxis = axis.ValueToAxisValue(d); } }));

        /// <summary>
        /// 最小值
        /// </summary>
        public double Min
        {
            get { return (double)GetValue(MinProperty); }
            internal set { SetValue(MinProperty, value); }
        }
        public static readonly DependencyProperty MinProperty = DependencyProperty.Register("Min", typeof(double), typeof(IAxis), new PropertyMetadata(0d, (sender, e) => { if (sender is IAxis axis && e.NewValue is double d) { axis.MinAxis = axis.ValueToAxisValue(d); } }));

        /// <summary>
        /// 坐标轴的最大值-转化坐标后
        /// </summary>
        public double MaxAxis { get; protected set; }
        /// <summary>
        /// 坐标轴的最小值-转化坐标后
        /// </summary>
        public double MinAxis { get; protected set; }

        /// <summary>
        /// 用于x或y轴分割
        /// </summary>
        internal AxisType XYType
        {
            get { return (AxisType)GetValue(XYTypeProperty); }
            set { SetValue(XYTypeProperty, value); }
        }
        internal static readonly DependencyProperty XYTypeProperty = DependencyProperty.Register("XYType", typeof(AxisType), typeof(IAxis), new PropertyMetadata(AxisType.X));
        /// <summary>
        /// 所属图表
        /// </summary>
        internal Chart OwnerChart { get; set; }
        #endregion

        public IAxis()
        {
            MaxAxis = ValueToAxisValue(Max);
            MinAxis = ValueToAxisValue(Min);
            this.Loaded += (sender, e) =>
            {
                Drawing();
                OwnerChart?.ChangedNeedRedrawing();
            };
        }

        #region function
        /// <summary>
        /// 实际值转换成坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public abstract double ValueToAxisValue(double value);
        /// <summary>
        /// 坐标值转换成实际值
        /// </summary>
        /// <param name="axisvalue"></param>
        /// <returns></returns>
        public abstract double AxisValueToValue(double axisvalue);
        /// <summary>
        /// 绘制数据分隔线
        /// </summary>
        /// <returns></returns>
        internal abstract void Drawing();
        /// <summary>
        /// 鼠标点位的坐标值
        /// </summary>
        /// <param name="point">鼠标点位</param>
        /// <returns></returns>
        public virtual double PointAxisValue(Point point)
        {
            double length = 1d;
            double pointLocation = 0d;
            switch (XYType)
            {
                case AxisType.X:
                    length = this.ActualWidth;
                    pointLocation = point.X;
                    break;
                case AxisType.Y:
                    length = this.ActualHeight;
                    pointLocation = length - point.Y;//y轴是反向的
                    break;
                default:
                    break;
            }
            return pointLocation * (MaxAxis - MinAxis) / length + MinAxis;
        }
        /// <summary>
        /// 鼠标点位的实际值
        /// </summary>
        /// <param name="point">鼠标点位</param>
        /// <returns></returns>
        public virtual double PointValue(Point point)
        {
            return AxisValueToValue(PointAxisValue(point));
        }

        #region 点位图形值，实际数值，实际坐标值相互转换
        /// <summary>
        /// 获取实际值转化成坐标值的参数，用于GetLocation()
        /// </summary>
        /// <returns></returns>
        public virtual ValueLocationConvertParam GetConvertParam()
        {
            return new ValueLocationConvertParam()
            {
                MaxAxis = MaxAxis,
                MinAxis = MinAxis,
                Length = XYType == AxisType.X ? this.ActualWidth : this.ActualHeight,
                XYType = XYType
            };
        }
        /// <summary>
        /// 获取真实数字对应的位置
        /// </summary>
        /// <param name="value">真实数据值</param>
        /// <returns></returns>
        public virtual double GetValueLocation(double value)
        {
            if (!IsLoaded) return default;
            return GetValueLocation(value, this.GetConvertParam());
        }
        /// <summary>
        /// 获取真实数据对应的坐标位置
        /// </summary>
        /// <param name="value">真实数据值</param>
        /// <param name="convert">转化参数，GetConvertParam()获取</param>
        /// <returns></returns>
        public virtual double GetValueLocation(double value, ValueLocationConvertParam convert)
        {
            value = ValueToAxisValue(value);
            return GetAxisValueLocation(value, convert);
        }
        /// <summary>
        /// 获取坐标数字对应的位置
        /// </summary>
        /// <param name="value">坐标数据</param>
        /// <returns></returns>
        public virtual double GetAxisValueLocation(double value)
        {
            if (!IsLoaded) return default;
            return GetAxisValueLocation(value, this.GetConvertParam());
        }
        /// <summary>
        /// 获取坐标数据对应的坐标位置
        /// </summary>
        /// <param name="value">数据值--xy轴坐标上的值</param>
        /// <param name="convert">转化参数，GetConvertParam()获取</param>
        /// <returns></returns>
        public virtual double GetAxisValueLocation(double value, ValueLocationConvertParam convert)
        {
            var temp = (value - convert.MinAxis) * convert.Length / (convert.MaxAxis - convert.MinAxis);
            return convert.XYType == AxisType.X ? temp : convert.Length - temp;
        }
        /// <summary>
        /// 获取点位对应的坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual double GetLocationAxisValue(double value)
        {
            return GetLocationAxisValue(value, GetConvertParam());
        }
        /// <summary>
        /// 获取点位对应的坐标值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="convert"></param>
        /// <returns></returns>
        public virtual double GetLocationAxisValue(double value, ValueLocationConvertParam convert)
        {
            return (convert.XYType == AxisType.X ? value : convert.Length - value) * (convert.MaxAxis - convert.MinAxis) / convert.Length + convert.MinAxis;
        }
        /// <summary>
        /// 获取点位对应的实际值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual double GetLocationValue(double value)
        {
            return GetLocationValue(value, GetConvertParam());
        }
        /// <summary>
        /// 获取点位对应的实际值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="convert"></param>
        /// <returns></returns>
        public virtual double GetLocationValue(double value, ValueLocationConvertParam convert)
        {
            return AxisValueToValue(GetLocationAxisValue(value, convert));
        }
        #endregion

        /// <summary>
        /// 移动图形
        /// </summary>
        /// <param name="x">x轴移动距离</param>
        /// <param name="y">y轴移动距离</param>
        internal virtual void Move(double x, double y)
        {
            if (!IsLoaded) return;
            double controlActualLength = 1;
            double movelength = 0d;
            switch (XYType)
            {
                case AxisType.X:
                    controlActualLength = this.ActualWidth;
                    movelength = x;
                    break;
                case AxisType.Y:
                    controlActualLength = this.ActualHeight;
                    movelength = -y;//纵坐标的方向在图形显示和轴上是反向的
                    break;
                default:
                    break;
            }
            var temp = (MaxAxis - MinAxis) * movelength / controlActualLength;
            Max = AxisValueToValue(MaxAxis - temp);
            Min = AxisValueToValue(MinAxis - temp);
            Drawing();
        }
        /// <summary>
        /// 放大缩小
        /// </summary>
        /// <param name="value"></param>
        /// <param name="point"></param>
        internal virtual void Zoom(double percent, Point point)
        {
            if (!IsLoaded) return;
            double controlLength = 1;
            double pv = 0d;
            switch (XYType)
            {
                case AxisType.X:
                    controlLength = this.ActualWidth;
                    pv = point.X;
                    break;
                case AxisType.Y:
                    controlLength = this.ActualHeight;
                    pv = this.ActualHeight - point.Y;
                    break;
                default:
                    break;
            }
            percent = 1 / percent;
            var v = (MaxAxis - MinAxis) * (pv / controlLength) + MinAxis;
            Min = AxisValueToValue(v - (v - MinAxis) * percent);
            Max = AxisValueToValue(v + (MaxAxis - v) * percent);
            Drawing();
        }
        #endregion

        /// <summary>
        /// 重置最大最小值
        /// </summary>
        /// <param name="max"></param>
        /// <param name="min"></param>
        public void ResetMaxMin(double max, double min)
        {
            this.Max = max;
            this.Min = min;
            this.Drawing();
            this.OwnerChart?.ChangedNeedRedrawing();
        }
        /// <summary>
        /// 手动刷新一下
        /// </summary>
        public void Redrawing()
        {
            this.Drawing();
            this.OwnerChart?.ChangedNeedRedrawing();
        }

        /// <summary>
        /// 根据轴名称创建对应的轴
        /// </summary>
        /// <param name="axisName"></param>
        /// <returns></returns>
        public static IAxis CreateFromAxisName(string axisName)
        {
            switch (axisName)
            {
                case "Log":
                    return new LogarithmicNumberAxis();
                case "LopBiexp":
                    return new LogicleBiexpAxis();
                case "Line":
                default:
                    return new LinearNumberAxis();
            }
        }
    }

    /// <summary>
    /// 刻度数据模型，用于图形显示
    /// </summary>
    internal abstract class IScaleData : NotifyPropertyChanged
    {
        private object _value;
        /// <summary>
        /// 名称
        /// </summary>
        public object Value
        {
            get { return _value; }
            set { _value = value; OnPropertyChanged("Value"); }
        }

        private double location;
        /// <summary>
        /// 坐标点所在位置的百分比
        /// </summary>
        public double Location
        {
            get { return location; }
            set { location = value; OnPropertyChanged("Location"); }
        }

        private int length;
        /// <summary>
        /// 分隔线长度
        /// </summary>
        public int Length
        {
            get { return length; }
            set { length = value; OnPropertyChanged("Length"); }
        }

        public IScaleData()
        {
            this.Length = 6;
        }
    }


    public struct ValueLocationConvertParam
    {
        public double MaxAxis { get; set; }
        public double MinAxis { get; set; }
        public double Length { get; set; }
        public AxisType XYType { get; set; }

    }
}
